package com.gjzhao.fmmall.service.timerTask;

import com.github.wxpay.sdk.WXPay;
import com.gjzhao.fmmall.config.PayConfig;
import com.gjzhao.fmmall.constant.Constants;
import com.gjzhao.fmmall.dao.OrdersMapper;
import com.gjzhao.fmmall.entity.Orders;
import com.gjzhao.fmmall.service.OrdersService;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import tk.mybatis.mapper.entity.Example;

/**
 * @Description: 超时订单关闭定时任务 //todo 尝试使用延时队列MQ解决
 * 接入quartz定时任务框架：每隔一段时间(指定时间)就去执行查询订单操作
 * 业务流程分析：
 *      - 1.配置查询订单状态的时间间隔
 *      - 2.从订单表查询所有未支付且订单创建时间超过30分钟的(根据当前时间减去30分钟，获取最早时间，只要在这个最早时间之前，都属于订单超时)
 *      - 3.遍历从订单表查询出来的订单信息，从微信平台查询每笔订单的支付状态(避免刚准备关闭订单，用户又支付了，但是订单状态还未及时更新的情况)
 *          - 3.1 如果该笔订单支付成功，则更新数据库订单状态为"已支付/待付款"
 *          - 3.2 如果该笔订单仍未支付，向微信平台发送请求关闭支付链接(避免刚从微信平台查询完订单状态为未支付，去准备关闭订单，这时用户又支付了)
 *      - 4.关闭订单
 *          - 4.1 修改订单表(修改订单状态为"已关闭"，关闭类型为"订单超时关闭")
 *          - 4.2 还原库存(根据订单id获取不同的商品订单快照，获取购物的商品数量，根据商品skuId获取现在库存，还原库存)
 *  注意点：
 *      - 1.关闭订单与还原库存属于一个事物操作，使用@Transactional
 *      - 2.在高并发下，可能存在多个用户都在关闭订单，需要操作同一变量，使用synchronized进行上锁操作，避免并发问题
 * @Author: gjzhao
 * @Date Created in 2022-01-15 8:33 上午
 */
@Component
public class OrderTimerCheckTask {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private OrdersService orderService;

    //抽取WxPay对象进行公用
    private WXPay wxPay = new WXPay(new PayConfig());

    //tips：可从 https://cron.qqe2.com 在线生成配置
    @Scheduled(cron = "0/5 * * * * ? ") //每5s查询一下订单表状态
    public void closeOrder() {
        logger.info("----------订单超时关闭定时任务开始执行[使用quartz框架实现]---------");

        //1.查询订单表：未支付且超时30min的订单
        //tips: 根据当前时间 - 30min = 最早时间，只要小于该最早时间都为超时
        Example example = new Example(Orders.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("status", Constants.OrderStats.NOT_PAYMENT);//未支付

        //计算最早时间
        long longTime = System.currentTimeMillis() - 30 * 60 * 1000;
        Date date = new Date(longTime);
        criteria.andLessThan("createTime", date);//超时
        //所有未支付且超时的订单信息
        List<Orders> orders = ordersMapper.selectByExample(example);

        logger.info("--------所有未支付且超时的的订单信息: [" + orders + "]-----------");

        try {
            for (Orders order : orders) {

                //2.关闭订单
                // a.情况一：当从订单查询出未支付且超时的订单，但是可能由于网络原因，某些订单其实已经支付成功，只是
                //          库里面的状态信息还未更新，此时如果如果关闭订单就是有问题的。
                //   解决办法：再次从微信支付平台查询订单的支付状态，如果已支付，则修改订单的状态为已支付，如果未支付再修改状态为已关闭

                Map<String, String> ordersParams = new HashMap<>();
                //商户号 + 订单号
                ordersParams.put("out_trade_no", order.getOrderId());
                Map<String, String> respMap = wxPay.orderQuery(ordersParams);
                String payStatus = respMap.get("trade_state");

                logger.info("---------订单号：" + order.getOrderId() + "当前的支付状态为:" + payStatus + "----------");

                if (Constants.PayStatus.SUCCESS.getValue().equalsIgnoreCase(payStatus)) {
                    //2.1 支付成功:修改订单状态为已支付
                    Orders updateOrder = new Orders();
                    updateOrder.setOrderId(order.getOrderId());
                    updateOrder.setStatus(Constants.OrderStats.NOT_DELIVERED.getValue()); //待发货
                    ordersMapper.updateByPrimaryKeySelective(updateOrder);

                    logger.info("--------订单号：" + order.getOrderId() + "已修改为已支付状态--------");

                } else if (Constants.PayStatus.NOTPAY.getValue().equalsIgnoreCase(payStatus)) {
                    //2.2 未支付：修改订单状态为已关闭
                    // b.情况二：当从微信支付平台查询到状态为未支付，且正准备关闭订单时，发现用户正好又支付了。
                    //   解决办法：当从微信支付平台查询到订单状态为未支付后，就向微信发起关闭订单支付链接的请求，用户不能再付款，此时应用在去修改订单状态

                    //2.2.1 向微信支付平台请求关闭支付订单
                    Map<String, String> closeResp = wxPay.closeOrder(ordersParams);
                    logger.info("---------微信支付平台关闭结果：[" + closeResp + "]----------");

                    //2.2.2 关闭订单
                    orderService.closeOrder(order.getOrderId());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
